#include "Communicate.h"
#include "../Clock/Clock.h"

#define true 1
#define false 0

Communicate* Com_Uart0 = NULL;
Communicate* Com_USCI0 = NULL;
Communicate* Com_USCI1 = NULL;
Communicate* Com_USCI2 = NULL;
Communicate* Com_USCI3 = NULL;
Communicate* Com_USCI4 = NULL;
#if sc95f_HAS_USCI5
Communicate* Com_USCI5 = NULL;
#endif

Communicate Com_Computer;
Communicate Com_ESP32;
Communicate Com_WaterDetector;

static Communicate* _getUser(uint8_t clinet)
{
    Communicate* com = NULL;
    switch (clinet)
    {
    case Computer_Uart:
        com = &Com_Computer;
        break;
    case ESP32_Uart:
        com = &Com_ESP32;
        break;
    case WaterDetector_Uart:
        com = &Com_WaterDetector;
        break;
    }
    return com;
}

static void _initCom(Communicate* Com)
{
    uint16_t i;
    Com -> begin_pointer = 0;
    Com -> end_pointer = 0;
    Com -> send_flag = 0;
    for(i=0;i<Communicate_BufSize;i++)
    {
        Com -> BUF[i] = 0;
    }
}

static Communicate* _getCom(uint8_t clinet)
{
    Communicate* com = NULL;
    switch (clinet)
    {
    case Communicate_Uart0:
        return Com_Uart0;
    case Communicate_USCI0:
        return Com_USCI0;
    case Communicate_USCI1:
        return Com_USCI1;
    case Communicate_USCI2:
        return Com_USCI2;
    case Communicate_USCI3:
        return Com_USCI3;
    case Communicate_USCI4:
        return Com_USCI4;
    #if sc95f_HAS_USCI5
    case Communicate_USCI5:
        return Com_USCI5;
    #endif
    }
    return NULL;
}

/**
 * @brief 默认初始化
 * 
 * @param clinet 用户类型: Computer_Uart 或 ESP32_Uart
 */
void Communicate_init(uint8_t clinet)
{
    switch (clinet)
    {
    case Communicate_Uart0:
        UART0_Init(Communicate_Fsys, Communicate_BaudRate, 
        Communicate_UartMode, Communicate_Uart0Clockmode, Communicate_UartRxMode);
        UART0_ITConfig(ENABLE, LOW);
        Com_Uart0 = _getUser(clinet);
        _initCom(Com_Uart0);
        break;
    case Communicate_USCI0:
        USCI0_UART_Init(Communicate_Fsys, Communicate_BaudRate, 
        Communicate_UartMode, Communicate_UartRxMode);
        USCI0_ITConfig(ENABLE, LOW);
        Com_USCI0 = _getUser(clinet);
        _initCom(Com_USCI0);
        break;
    case Communicate_USCI1:
        USCI1_UART_Init(Communicate_Fsys, Communicate_BaudRate, 
        Communicate_UartMode, Communicate_UartRxMode);
        USCI1_ITConfig(ENABLE, LOW);
        Com_USCI1 = _getUser(clinet);
        _initCom(Com_USCI1);
        break;
    case Communicate_USCI2:
        USCI2_UART_Init(Communicate_Fsys, Communicate_BaudRate, 
        Communicate_UartMode, Communicate_UartRxMode);
        USCI2_ITConfig(ENABLE, LOW);
        Com_USCI2 = _getUser(clinet);
        _initCom(Com_USCI2);
        break;
    case Communicate_USCI3:
        USCI3_UART_Init(Communicate_Fsys, Communicate_BaudRate, 
        Communicate_UartMode, Communicate_UartRxMode);
        USCI3_ITConfig(ENABLE, LOW);
        Com_USCI3 = _getUser(clinet);
        _initCom(Com_USCI3);
        break;
    case Communicate_USCI4:
        USCI4_UART_Init(Communicate_Fsys, Communicate_BaudRate, 
        Communicate_UartMode, Communicate_UartRxMode);
        USCI4_ITConfig(ENABLE, LOW);
        Com_USCI4 = _getUser(clinet);
        _initCom(Com_USCI4);
        break;
    #if sc95f_HAS_USCI5
    case Communicate_USCI5:
        USCI5_UART_Init(Communicate_Fsys, Communicate_BaudRate, 
        Communicate_UartMode, Communicate_UartRxMode);
        USCI5_ITConfig(ENABLE, LOW);
        Com_USCI5 = _getUser(clinet);
        _initCom(Com_USCI5);
        break;
    #endif
    }
}

/**
 * @brief 带参数初始化
 * 
 * @param clinet 用户类型:Computer_Uart 或 ESP32_Uart 或 WaterDetector_Uart
 * @param Fsys 系统频率(Hz)
 * @param BaudRate 波特率
 * @param UartMode 串口模式:UART_Mode_10B 或 UART_Mode_11B
 * @param ClockMode 时钟挑选:仅Uart0: UART0_CLOCK_TIMER1 或 UART0_CLOCK_TIMER2
 * @param UartRxMode Rx允许否:UART_RX_ENABLE 或 UART_RX_DISABLE
 */
void Communicate_initSet(uint8_t clinet, uint32_t Fsys, uint32_t BaudRate, uint8_t UartMode, uint8_t ClockMode, uint8_t UartRxMode)
{
    switch (clinet)
    {
    case Communicate_Uart0:
        UART0_Init(Fsys, BaudRate, 
        UartMode, ClockMode, UartRxMode);
        UART0_ITConfig(ENABLE, LOW);
        Com_Uart0 = _getUser(clinet);
        _initCom(Com_Uart0);
        break;
    case Communicate_USCI0:
        USCI0_UART_Init(Fsys, BaudRate, 
        UartMode, UartRxMode);
        USCI0_ITConfig(ENABLE, LOW);
        Com_USCI0 = _getUser(clinet);
        _initCom(Com_USCI0);
        break;
    case Communicate_USCI1:
        USCI1_UART_Init(Fsys, BaudRate, 
        UartMode, UartRxMode);
        USCI1_ITConfig(ENABLE, LOW);
        Com_USCI1 = _getUser(clinet);
        _initCom(Com_USCI1);
        break;
    case Communicate_USCI2:
        USCI2_UART_Init(Fsys, BaudRate, 
        UartMode, UartRxMode);
        USCI2_ITConfig(ENABLE, LOW);
        Com_USCI2 = _getUser(clinet);
        _initCom(Com_USCI2);
        break;
    case Communicate_USCI3:
        USCI3_UART_Init(Fsys, BaudRate, 
        UartMode, UartRxMode);
        USCI3_ITConfig(ENABLE, LOW);
        Com_USCI3 = _getUser(clinet);
        _initCom(Com_USCI3);
        break;
    case Communicate_USCI4:
        USCI4_UART_Init(Fsys, BaudRate, 
        UartMode, UartRxMode);
        USCI4_ITConfig(ENABLE, LOW);
        Com_USCI4 = _getUser(clinet);
        _initCom(Com_USCI4);
        break;
    #if sc95f_HAS_USCI5
    case Communicate_USCI5:
        USCI5_UART_Init(Fsys, BaudRate, 
        UartMode, UartRxMode);
        USCI5_ITConfig(ENABLE, LOW);
        Com_USCI5 = _getUser(clinet);
        _initCom(Com_USCI5);
        break;
    #endif
    }
}

uint16_t Communicate_available(uint8_t clinet)
{
    Communicate* com = _getCom(clinet);
    return (com -> end_pointer - com -> begin_pointer + Communicate_BufSize) % Communicate_BufSize;
}

bool Communicate_sendData8(uint8_t clinet, uint8_t data8)
{
    uint16_t i=0;
    Communicate* com = _getCom(clinet);
    switch (clinet)
    {
    case Communicate_Uart0:
        UART0_SendData8(data8);
        break;
    case Communicate_USCI0:
        USCI0_UART_SendData8(data8);
        break;
    case Communicate_USCI1:
        USCI1_UART_SendData8(data8);
        break;
    case Communicate_USCI2:
        USCI2_UART_SendData8(data8);
        break;
    case Communicate_USCI3:
        USCI3_UART_SendData8(data8);
        break;
    case Communicate_USCI4:
        USCI4_UART_SendData8(data8);
        break;
    #if sc95f_HAS_USCI5
    case Communicate_USCI5:
        USCI5_UART_SendData8(data8);
        break;
    #endif
    }
    for(i=0;i<1000;i++)
    {
        if(com->send_flag)
        {
            com->send_flag = false;
            return true;
        }
    }
    return false;
}

uint8_t Communicate_receiveData8(uint8_t clinet)
{
    switch (clinet)
    {
    case Communicate_Uart0:
        return UART0_ReceiveData8();
        break;
    case Communicate_USCI0:
        return USCI0_UART_ReceiveData8();
        break;
    case Communicate_USCI1:
        break;
    case Communicate_USCI2:
        break;
    case Communicate_USCI3:
        break;
    case Communicate_USCI4:
        break;
    #if sc95f_HAS_USCI5
    case Communicate_USCI5:
        break;
    #endif
    }
    return 0;
}

void Communicate_sendString8(uint8_t clinet, const uint8_t* str)
{
    while(*str)
    {
        Communicate_sendData8(clinet, *str);
        str++;
    }
}

uint16_t Communicate_receiveString8(uint8_t clinet, uint8_t* str, uint16_t sizestr)
{
    Communicate* com = _getCom(clinet);
    uint16_t i;
    uint16_t buf_num;

    buf_num = Communicate_available(clinet);

    if(sizestr > buf_num + 1)
    {
        sizestr = buf_num + 1;
    }
    
    if(sizestr <= 1)
    {
        return 0;
    }
    
    for (i = 0; i < sizestr - 1; i++)
    {
        (str)[i] = com -> BUF[com->begin_pointer];
        com -> begin_pointer = (com -> begin_pointer + 1) % Communicate_BufSize;
    }
    (str)[sizestr - 1] = '\0';
    return sizestr;
}

#if Communicate_IncludeTest(Communicate_Uart0)

void Uart0_interrupt(void) interrupt 4
{
    if(UART0_GetFlagStatus(UART0_FLAG_RI))
    {
        if(Communicate_available(Communicate_Uart0) < Communicate_BufSize)
        {
            Com_Uart0 -> BUF[Com_Uart0 -> end_pointer] = UART0_ReceiveData8();
            Com_Uart0 -> end_pointer = (Com_Uart0 -> end_pointer + 1) % Communicate_BufSize;
        }
        UART0_ClearFlag(UART0_FLAG_RI);
    }
    else if(UART0_GetFlagStatus(UART0_FLAG_TI))
    {
        Com_Uart0 -> send_flag = true;
        UART0_ClearFlag(UART0_FLAG_TI);
    }
}

#endif

#if Communicate_IncludeTest(Communicate_USCI0)

void USCI0_interrupt(void) interrupt 7
{
    if(USCI0_GetFlagStatus(USCI0_UART_FLAG_RI))
    {
        if(Communicate_available(Communicate_USCI0) < Communicate_BufSize)
        {
            Com_USCI0 -> BUF[Com_USCI0 -> end_pointer] = USCI0_UART_ReceiveData8();
            Com_USCI0 -> end_pointer = (Com_USCI0 -> end_pointer + 1) % Communicate_BufSize;
        }
        USCI0_ClearFlag(USCI0_UART_FLAG_RI);
    }
    else if(USCI0_GetFlagStatus(USCI0_UART_FLAG_TI))
    {
        Com_USCI0 -> send_flag = true;
        USCI0_ClearFlag(USCI0_UART_FLAG_TI);
    }
}

#endif

#if Communicate_IncludeTest(Communicate_USCI1)

void USCI1_interrupt(void) interrupt 15
{
    if(USCI1_GetFlagStatus(USCI1_UART_FLAG_RI))
    {
        if(Communicate_available(Communicate_USCI1) < Communicate_BufSize)
        {
            Com_USCI1 -> BUF[Com_USCI1 -> end_pointer] = USCI1_UART_ReceiveData8();
            Com_USCI1 -> end_pointer = (Com_USCI1 -> end_pointer + 1) % Communicate_BufSize;
        }
        USCI1_ClearFlag(USCI1_UART_FLAG_RI);
    }
    else if(USCI1_GetFlagStatus(USCI1_UART_FLAG_TI))
    {
        Com_USCI1 -> send_flag = true;
        USCI1_ClearFlag(USCI1_UART_FLAG_TI);
    }
}

#endif

#if Communicate_IncludeTest(Communicate_USCI2)

void USCI2_interrupt(void) interrupt 16
{
    if(USCI2_GetFlagStatus(USCI2_UART_FLAG_RI))
    {
        if(Communicate_available(Communicate_USCI2) < Communicate_BufSize)
        {
            Com_USCI2 -> BUF[Com_USCI2 -> end_pointer] = USCI2_UART_ReceiveData8();
            Com_USCI2 -> end_pointer = (Com_USCI2 -> end_pointer + 1) % Communicate_BufSize;
        }
        USCI2_ClearFlag(USCI2_UART_FLAG_RI);
    }
    else if(USCI2_GetFlagStatus(USCI2_UART_FLAG_TI))
    {
        Com_USCI2 -> send_flag = true;
        USCI2_ClearFlag(USCI2_UART_FLAG_TI);
    }
}

#endif

#if Communicate_IncludeTest(Communicate_USCI3)

void USCI3_interrupt(void) interrupt 17
{
    if(USCI3_GetFlagStatus(USCI3_UART_FLAG_RI))
    {
        if(Communicate_available(Communicate_USCI3) < Communicate_BufSize)
        {
            Com_USCI3 -> BUF[Com_USCI3 -> end_pointer] = USCI3_UART_ReceiveData8();
            Com_USCI3 -> end_pointer = (Com_USCI3 -> end_pointer + 1) % Communicate_BufSize;
        }
        USCI3_ClearFlag(USCI3_UART_FLAG_RI);
    }
    else if(USCI3_GetFlagStatus(USCI3_UART_FLAG_TI))
    {
        Com_USCI3 -> send_flag = true;
        USCI3_ClearFlag(USCI3_UART_FLAG_TI);
    }
}

#endif

#if Communicate_IncludeTest(Communicate_USCI4)

void USCI4_interrupt(void) interrupt 18
{
    if(USCI4_GetFlagStatus(USCI4_UART_FLAG_RI))
    {
        if(Communicate_available(Communicate_USCI4) < Communicate_BufSize)
        {
            Com_USCI4 -> BUF[Com_USCI4 -> end_pointer] = USCI4_UART_ReceiveData8();
            Com_USCI4 -> end_pointer = (Com_USCI4 -> end_pointer + 1) % Communicate_BufSize;
        }
        USCI4_ClearFlag(USCI4_UART_FLAG_RI);
    }
    else if(USCI4_GetFlagStatus(USCI4_UART_FLAG_TI))
    {
        Com_USCI4 -> send_flag = true;
        USCI4_ClearFlag(USCI4_UART_FLAG_TI);
    }
}

#endif

#if (Communicate_IncludeTest(Communicate_USCI5) && sc95f_HAS_USCI5)

void USCI5_interrupt(void) interrupt 19
{
    if(USCI5_GetFlagStatus(USCI5_UART_FLAG_RI))
    {
        if(Communicate_available(Communicate_USCI5) < Communicate_BufSize)
        {
            Com_USCI5 -> BUF[Com_USCI5 -> end_pointer] = USCI5_UART_ReceiveData8();
            Com_USCI5 -> end_pointer = (Com_USCI5 -> end_pointer + 1) % Communicate_BufSize;
        }
        USCI5_ClearFlag(USCI5_UART_FLAG_RI);
    }
    else if(USCI5_GetFlagStatus(USCI5_UART_FLAG_TI))
    {
        Com_USCI5 -> send_flag = true;
        USCI5_ClearFlag(USCI5_UART_FLAG_TI);
    }
}

#endif
